package com.yykj.pay.server.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.github.wxpay.sdk.WxConfigCustom;
import com.yykj.common.enums.ResultEnums;
import com.yykj.common.exception.NormalException;
import com.yykj.common.service.RedisService;
import com.yykj.common.util.HttpUtil;
import com.yykj.common.util.LocalStringUtil;
import com.yykj.message.center.client.dto.TDMQMessageSendResult;
import com.yykj.message.center.client.enums.OrderType;
import com.yykj.message.center.client.feign.MessageFeignClient;
import com.yykj.pay.center.client.dto.ClientPayConfigDTO;
import com.yykj.pay.center.client.form.PayCreateForm;
import com.yykj.pay.server.constant.YyWxConfig;
import com.yykj.pay.server.entity.Order;
import com.yykj.pay.server.repository.OrderMapper;
import com.yykj.pay.server.service.ClientPayConfigService;
import com.yykj.pay.server.service.PaymentService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Component
@Transactional
@Slf4j
public class PaymentServiceImpl implements PaymentService {



   /* @Autowired
    WXPay wxPay;*/

    @Autowired
    OrderMapper orderMapper;

    @Autowired
    RedisService redisService;

    @Autowired
    MessageFeignClient messageFeignClient;
    @Autowired
    ClientPayConfigService clientPayConfigService;

    @Value("${wxpay.notifyUrl}")
    private String wxNotifyUrl;

    /*@Override
    public Map<String, String> create(PayCreateForm payCreateForm) {
        try {
            String orderNo = payCreateForm.getOrderNo();
            Map<String, String> returnMap = new HashMap<>();
            Map<String, String> data = new HashMap<>();
            data.put("openid", payCreateForm.getOpenId());

            data.put("body", payCreateForm.getDescription());
            data.put("out_trade_no", orderNo);
            data.put("total_fee", payCreateForm.getAmount() + "");
            data.put("notify_url", wxPayInfo.getNotifyUrl() + orderNo);
            data.put("trade_type", "JSAPI");
//            data.put("attach", payCreateForm.getAttach());
            data.put("nonce_str", WXPayUtil.generateNonceStr());
            data.put("appid", wxPayInfo.getAppId());
            data.put("mch_id", wxPayInfo.getMchId());
            data.put("sign_type", WXPayConstants.MD5);
            String s = WXPayUtil.generateSignature(data, wxPayInfo.getKey());
            data.put("sign", s);
            log.info("发起微信支付 => {}", JsonUtil.toJson(data));
            Map<String, String> response = wXPay.unifiedOrder(data);
            log.info("微信支付返回 => {}", JsonUtil.toJson(response));
            if ("SUCCESS".equals(response.get("return_code"))) {
                if ("SUCCESS".equals(response.get("result_code"))) {
                    String prepayId = response.get("prepay_id");
                    Map<String, String> payMap = new HashMap<String, String>();
                    payMap.put("appId", wxPayInfo.getAppId());
                    payMap.put("timeStamp", PayUtil.getCurrentTimestamp() + "");
                    payMap.put("nonceStr", PayUtil.generateNonceStr());
                    payMap.put("signType", WXPayConstants.HMACSHA256);
                    payMap.put("package", "prepay_id=" + prepayId);
                    String paySign = WXPayUtil.generateSignature(payMap, wxPayInfo.getKey(), WXPayConstants.SignType.HMACSHA256);
                    payMap.put("paySign", paySign);
                    //成功在微信下单的再入本地裤
                    Order order = Order.builder()
                            .id(LocalStringUtil.getUUID())
                            .openid(payCreateForm.getOpenId())
                            .createTime(LocalDateTime.now())
                            .orderNo(payCreateForm.getOrderNo()).build();
                    try {
                        this.orderMapper.insert(order);
                    }catch (Exception e){
                        e.printStackTrace();
                    }

                    return payMap;
                }else{
                    throw new RuntimeException(response.get("err_code_des"));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new NormalException(ResultEnums.ORDER_ERROR);
    }*/

    @Override
    public Map<String, String> create(PayCreateForm payCreateForm) {
        try {
            ClientPayConfigDTO clientPayConfigDTO = this.clientPayConfigService.get(payCreateForm.getHospitalId() + "");

            if (clientPayConfigDTO == null) {
                throw new RuntimeException("该医院配置不存在");
            }
            WxConfigCustom config = new WxConfigCustom(clientPayConfigDTO.getClientAppId(), clientPayConfigDTO.getClientMchId(), clientPayConfigDTO.getClientMechKey(), clientPayConfigDTO.getClientMechPem());
            WXPay wxPay = new WXPay(config);

            // 1. 拼接统⼀下单地址参数

            Map<String, String> paraMap = new HashMap<String, String>();
            paraMap.put("openid", payCreateForm.getOpenId());//用户标识
            paraMap.put("body", "预约挂号"); // 商品描述
            paraMap.put("out_trade_no", payCreateForm.getOrderNo());// 订单号
            //金额转换

            paraMap.put("total_fee", payCreateForm.getAmount() + ""); // ⽀付⾦额，单位分，即0.01元
            paraMap.put("notify_url", wxNotifyUrl + payCreateForm.getOrderNo());//支付结果通知地址
            paraMap.put("trade_type", "JSAPI"); // 交易类型
            log.info("统一下单请求参数:{}", JSON.toJSONString(paraMap));
            //2.发送post请求"统⼀下单接⼝", 返回预⽀付id:prepay_id
            Map<String, String> map = wxPay.unifiedOrder(paraMap);

            //3.将数据组合,再次签名
            Map<String, String> payMap = new HashMap<String, String>();
            String prepayId = (String) map.get("prepay_id");
            if ("OK".equals(map.get("return_msg"))) {
                payMap.put("appId", YyWxConfig.APP_ID);
                payMap.put("timeStamp", WXPayUtil.getCurrentTimestamp() + "");
                payMap.put("nonceStr", WXPayUtil.generateNonceStr());
                payMap.put("signType", WXPayConstants.HMACSHA256);
                payMap.put("package", "prepay_id=" + prepayId);
                //通过appId, timeStamp, nonceStr, signType, package及商户密钥进⾏key=value形式拼接并加密
                String paySign = WXPayUtil.generateSignature(payMap, YyWxConfig.KEY, WXPayConstants.SignType.HMACSHA256);
                payMap.put("paySign", paySign);
                //成功在微信下单的再入本地裤
                Order order = Order.builder()
                        .id(LocalStringUtil.getUUID())
                        .openid(payCreateForm.getOpenId())
                        .payAmount(payCreateForm.getAmount())
                        .createTime(LocalDateTime.now())
                        .orderNo(payCreateForm.getOrderNo()).build();
                try {
                    this.orderMapper.insert(order);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                log.info("wxPay支付请求参数 => {}", JSON.toJSONString(payMap));
                log.info("调用统一下单接口,成功！");
            } else {
                log.error("调用统一下单接口,失败！");
                throw new NormalException(ResultEnums.ORDER_ERROR);
            }
            return payMap;
        } catch (Exception e) {
            e.printStackTrace();
            throw new NormalException(ResultEnums.TASK_DATA_ERROR);
        }

    }

    @Override
    public void callback(String orderNo) {
        String lockkey = "lockOrderCallback" + orderNo;
        String lockValue = getLock(lockkey);
        Order order = this.orderMapper.findByOrderNo(orderNo);
        if (order.getNotifyed() != 1) {
            order.setPayTime(LocalDateTime.now());
            order.setNotifyed(1);
            order.setStatus(1);
            this.orderMapper.updateById(order);
            try {
                String orderId = orderNo;
                TDMQMessageSendResult tdmqMessageSendResult = this.messageFeignClient.sendOrderPaySuccessMessage(orderId, OrderType.drug);
                if (tdmqMessageSendResult != null && StringUtils.isNotBlank(tdmqMessageSendResult.getMsgId())) {
                    order.setBizNotifyed(1);
                    this.orderMapper.updateById(order);
                }
            } catch (Exception e) {
                log.info("tdmq sendOrderPaySuccessMessage发送失败");
                e.printStackTrace();
            }
        }
        this.redisService.unlock(lockkey, lockValue);
    }

    @Override
    @SneakyThrows
    public void testMq(String orderNo) {
        this.messageFeignClient.sendOrderPaySuccessMessage(orderNo, OrderType.drug);
    }

    @Override
    @SneakyThrows
    public boolean refund(String orderNo, int hospitalId) {
        ClientPayConfigDTO clientPayConfigDTO = this.clientPayConfigService.get(hospitalId + "");
        if (clientPayConfigDTO == null) {
            throw new RuntimeException("该医院配置不存在");
        }
        WxConfigCustom config = new WxConfigCustom(clientPayConfigDTO.getClientAppId(), clientPayConfigDTO.getClientMchId(), clientPayConfigDTO.getClientMechKey(), clientPayConfigDTO.getClientMechPem());
        WXPay wxPay = new WXPay(config);
        // 1. 拼接统⼀下单地址参数
        Order order = this.orderMapper.findByOrderNo(orderNo);
        Map<String, String> paraMap = new HashMap<String, String>();
        paraMap.put("out_trade_no", orderNo);//用户标识
        paraMap.put("out_refund_no", UUID.randomUUID().toString().replaceAll("-", "")); // 商品描述
        paraMap.put("total_fee", order.getPayAmount() + "");
        paraMap.put("refund_fee", order.getPayAmount() + "");
        //金额转换

        paraMap.put("notify_url", wxNotifyUrl + orderNo);//支付结果通知地址
        log.info("退款请求参数:{}", JSON.toJSONString(paraMap));
        //2.发送post请求"统⼀下单接⼝", 返回预⽀付id:prepay_id
        try {
            Map<String, String> refundResult = wxPay.refund(paraMap);
            //3.将数据组合,再次签名
            if ("SUCCESS".equals(refundResult.get("return_code")) && "SUCCESS".equals(refundResult.get("result_code"))) {

                order.setStatus(2);
                order.setRefundTime(LocalDateTime.now());
                this.orderMapper.updateById(order);
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;

    }

    @Override
    @SneakyThrows
    public boolean refundForUsercenter(String order, int fee, int hospitalId) {
        // 1. 拼接统⼀下单地址参数
        ClientPayConfigDTO clientPayConfigDTO = this.clientPayConfigService.get(hospitalId + "");
        if (clientPayConfigDTO == null) {
            throw new RuntimeException("该医院配置不存在");
        }
        WxConfigCustom config = new WxConfigCustom(clientPayConfigDTO.getClientAppId(), clientPayConfigDTO.getClientMchId(), clientPayConfigDTO.getClientMechKey(), clientPayConfigDTO.getClientMechPem());
        WXPay wxPay = new WXPay(config);
        Map<String, String> paraMap = new HashMap<String, String>();
        paraMap.put("out_trade_no", order);//用户标识
        paraMap.put("out_refund_no", UUID.randomUUID().toString().replaceAll("-", "")); // 商品描述
        paraMap.put("total_fee", fee + "");
        paraMap.put("refund_fee", fee + "");
        paraMap.put("notify_url", wxNotifyUrl + order);//支付结果通知地址
        log.info("退款请求参数:{}", JSON.toJSONString(paraMap));
        //2.发送post请求"统⼀下单接⼝", 返回预⽀付id:prepay_id
        try {
            Map<String, String> refundResult = wxPay.refund(paraMap);
            //3.将数据组合,再次签名
            if ("SUCCESS".equals(refundResult.get("return_code")) && "SUCCESS".equals(refundResult.get("result_code"))) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    private String getLock(String key) {
        boolean lockStatus = false;
        int cnt = 0;
        String lockValue = "";
        do {
            cnt++;
            lockValue = "" + (System.currentTimeMillis() + 2000);
            lockStatus = redisService.lock(key, lockValue);
            if (!lockStatus) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        } while (cnt < 5 && !lockStatus);
        if (!lockStatus) {
            throw new NormalException(ResultEnums.REDIS_LOCK_ERROR);
        } else {
            return lockValue;
        }
    }

    public static void main(String[] args) throws Exception {
        String[] a = {"100131120220410145150082", "100131220220409211719034", "100131320220409195621256", "100131320220409185346185", "100131120220409181614808", "100131120220409181305076", "100131320220409164527739", "100131320220409164139985", "100131320220409163036071", "100131320220409151500816", "100131120220409150200754", "100131220220409144642395", "100131120220409135002598", "100131220220409124900280", "100131320220409123734397", "100131320220409112139256", "100131120220409095612985", "100131120220409090554431", "100131220220408183836816", "100131120220408183404775", "100131220220408180719437", "100131320220408173852207", "100131220220408171404371", "100131320220408155222870", "100131320220408153926680", "100131320220408153623442", "100131120220408152233526", "100131320220408151947120", "100131320220408150150233", "100131120220408141352739", "100131120220408134339395", "100131120220408133106911", "100131120220408104217097", "100131120220408095320364", "100131120220408091154007", "100131120220408090234037", "100131120220407201615061", "100131220220407200714920", "100131320220407192130178", "100131220220407190516014", "100131320220407183234044", "100131120220407173758481", "100131120220407172729325", "100131120220407172723696", "100131320220407170504932", "100131120220407163832379", "100131120220407163559010", "100131120220407163314854", "100131120220407155503680", "100131120220407141354842", "100131320220407140623429", "100131320220407134227599", "100131120220407111508204", "100131120220407110532012", "100131120220407103915551", "100131120220407095728688", "100131220220407095133506", "100131120220407095128376", "100131120220407094847154", "100131120220407094801729", "100131220220407094606547", "100131120220407094224205", "100131120220406191527064", "10013null20220402195112448", "100131120220402194711556", "100131120220402194311513", "100131220220401220513752", "10013null20220401195730083", "100131220220401184312302", "100131220220401175352402", "100131220220401174809925", "100131120220331140819815", "100131120220331112614781", "100131120220331105033375", "100131120220331100916274", "100131120220331093602162", "100131120220330183105245", "100131120220330174611860", "100131120220329134612708", "100131120220328175753965", "100131120220328174358322", "100131120220328161054152", "100131320220129200938470", "100131320220129173026853", "100131320220129171320076", "100131320220129171229397", "100131320220129171002068", "100131320220129153155478", "100131320220111194650269", "100131120220111194448984", "100131120220111101404350", "100131120211224103507816", "100131120211224103125224", "100131120211224100810209", "100131120211224092358690", "100131120211224091230923", "100131320211224003803405", "100131320211224001447556", "100131320211223232936212", "100131220211223231658889", "100131120211223231216432", "100131120211223224153619", "100131120211223223510287", "100131120211223221604600", "100131120211223220333338", "100131120211223214411277", "100131120211223211917267"};

        for (String b : a) {
            HttpUtil.get("http://localhost:9013//pay/refund/" + b);
        }

    }
}
